package icu.d4peng.basic.database.druid;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import com.alibaba.druid.support.spring.stat.DruidStatInterceptor;
import icu.d4peng.basic.database.druid.properties.DruidProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.Advisor;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.aop.support.RegexpMethodPointcutAdvisor;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.Assert;

import javax.sql.DataSource;

/**
 * DruidDataSourceAutoConfigure:Druid数据源自动配置
 *
 * @author d4peng <a href="d4peng@qq.com">d4peng@qq.com</a>
 * @since 2023.12.02
 */
@Configuration
@ConditionalOnClass(DruidDataSource.class)
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
@EnableConfigurationProperties({ DruidProperties.class, DataSourceProperties.class, })
public class DruidDataSourceAutoConfigure {

	private static final Logger LOGGER = LoggerFactory.getLogger(DruidDataSourceAutoConfigure.class);

	private static final String DEFAULT_ALLOW_IP = "127.0.0.1";

	private final DruidProperties druidProperties;

	public DruidDataSourceAutoConfigure(DruidProperties druidProperties) {
		Assert.notNull(druidProperties, "druid datasource can't be null!");
		this.druidProperties = druidProperties;
	}

	@Bean(initMethod = "init")
	@ConditionalOnMissingBean
	public DataSource dataSource() {
		LOGGER.info("Init DruidDataSource");
		return this.druidProperties;
	}

	/**
	 * 视图统计
	 * @return 视图统计
	 */
	@Bean
	@ConditionalOnWebApplication
	@ConditionalOnProperty(prefix = DruidProperties.PREFIX + ".stat-view-servlet", name = "enabled",
			havingValue = "true", matchIfMissing = true)
	public ServletRegistrationBean<?> statViewServletRegistrationBean() {
		DruidProperties.StatViewServlet statViewServlet = druidProperties.getStatViewServlet();
		ServletRegistrationBean<StatViewServlet> registrationBean = new ServletRegistrationBean<>();
		registrationBean.setServlet(new StatViewServlet());
		registrationBean
			.addUrlMappings(statViewServlet.getUrlPattern() != null ? statViewServlet.getUrlPattern() : "/druid/*");
		if (statViewServlet.getAllow() != null) {
			registrationBean.addInitParameter("allow", statViewServlet.getAllow());
		}
		else {
			registrationBean.addInitParameter("allow", DEFAULT_ALLOW_IP);
		}
		if (statViewServlet.getDeny() != null) {
			registrationBean.addInitParameter("deny", statViewServlet.getDeny());
		}
		if (statViewServlet.getLoginUsername() != null) {
			registrationBean.addInitParameter("loginUsername", statViewServlet.getLoginUsername());
		}
		if (statViewServlet.getLoginPassword() != null) {
			registrationBean.addInitParameter("loginPassword", statViewServlet.getLoginPassword());
		}
		if (statViewServlet.getResetEnable() != null) {
			registrationBean.addInitParameter("resetEnable", statViewServlet.getResetEnable());
		}
		return registrationBean;
	}

	/**
	 * 视图统计拦截
	 * @return 视图统计拦截
	 */
	@Bean
	@ConditionalOnWebApplication
	@ConditionalOnProperty(prefix = DruidProperties.PREFIX + ".web-stat-filter", name = "enabled", havingValue = "true",
			matchIfMissing = true)
	public FilterRegistrationBean<WebStatFilter> webStatFilterRegistrationBean() {
		DruidProperties.WebStatFilter webStatFilter = druidProperties.getWebStatFilter();
		FilterRegistrationBean<WebStatFilter> registrationBean = new FilterRegistrationBean<>();
		WebStatFilter filter = new WebStatFilter();
		registrationBean.setFilter(filter);
		registrationBean.addUrlPatterns(webStatFilter.getUrlPattern() != null ? webStatFilter.getUrlPattern() : "/*");
		registrationBean.addInitParameter("exclusions", webStatFilter.getExclusions() != null
				? webStatFilter.getExclusions() : "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
		if (webStatFilter.getSessionStatEnable() != null) {
			registrationBean.addInitParameter("sessionStatEnable", webStatFilter.getSessionStatEnable());
		}
		if (webStatFilter.getSessionStatMaxCount() != null) {
			registrationBean.addInitParameter("sessionStatMaxCount", webStatFilter.getSessionStatMaxCount());
		}
		if (webStatFilter.getPrincipalSessionName() != null) {
			registrationBean.addInitParameter("principalSessionName", webStatFilter.getPrincipalSessionName());
		}
		if (webStatFilter.getPrincipalCookieName() != null) {
			registrationBean.addInitParameter("principalCookieName", webStatFilter.getPrincipalCookieName());
		}
		if (webStatFilter.getProfileEnable() != null) {
			registrationBean.addInitParameter("profileEnable", webStatFilter.getProfileEnable());
		}
		return registrationBean;
	}

	/**
	 * 切面拦截
	 * @return 切面拦截
	 */
	@ConditionalOnProperty(prefix = DruidProperties.PREFIX, name = "aop-patterns")
	@Bean
	public Advisor advisor() {
		return new RegexpMethodPointcutAdvisor(druidProperties.getAopPatterns(), new DruidStatInterceptor());
	}

	@ConditionalOnProperty(name = "spring.aop.auto", havingValue = "false")
	@Bean
	public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
		advisorAutoProxyCreator.setProxyTargetClass(true);
		return advisorAutoProxyCreator;
	}

}
